/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       shared_file.h

	$Header: /game/shared_file.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "shared_file.h"

#include "format_string.h"
#include "game_time.h"
#include "os_file.h"
#include "standard_c_file.h"

int const k_buffer_size = 1024*16;

// ----------------------------------------------------------
// a file which is designed to be shared between several objects or threads
// ------------------------------------------------------------

t_shared_file::t_shared_file()
{}

t_shared_file::t_shared_file( std::string filename, std::string mode, t_derived_file_systems file_system) : m_file_ptr(NULL)
{
	switch (file_system)
	{
	case k_windows_file_sys:
		m_file_ptr.reset(new t_os_file);
		break;

	case k_standard_c_file_sys: default:
		m_file_ptr.reset(new t_standard_c_file);
		break;
	}
	
	m_file_ptr->open(filename.c_str(), mode.c_str());
	m_filename		= filename;
	m_file_system	= file_system;
}

t_shared_file::~t_shared_file()
{
	if (m_file_ptr.get() != 0)
		m_file_ptr->close();
}

// -------------------------------------------------------------------------------------------------------------------------
// a specialized shared resource file class for the resource editor.
// -------------------------------------------------------------------------------------------------------------------------

t_resource_shared_file::t_resource_shared_file() : t_shared_file()
{}

t_resource_shared_file::t_resource_shared_file( std::string filename, std::string mode , t_derived_file_systems file_system)
: t_shared_file(filename, mode, file_system)
{}

bool t_resource_shared_file::open( const char *filename, const char *mode , t_derived_file_systems file_system)
{
	m_file_system = file_system;

	switch (m_file_system)
	{
	case k_windows_file_sys:
		m_file_ptr.reset(new t_os_file);
		break;
		
	case k_standard_c_file_sys: default:
		m_file_ptr.reset(new t_standard_c_file);
		break;
	}
	
	bool rval = m_file_ptr->open(filename, mode);
	
	if (rval == true)
		m_filename	= filename;

	return rval;
}

bool t_resource_shared_file::close( )
{
	bool rval = true;

	if (m_file_ptr.get() != 0)
		rval = m_file_ptr->close();

	return rval;
}

bool t_resource_shared_file::is_open()
{
	if (m_file_ptr.get() == 0) 
		return false;

	return m_file_ptr->is_open();
}

// ----------------------------------------------------------
// streambuf which uses t_shared_file
// ------------------------------------------------------------
t_shared_file_buffer::t_shared_file_buffer( t_shared_file* file )
                    : m_buffer( new char[k_buffer_size] )
{
	m_file = file;
	m_file_position = 0;

	setg( m_buffer.get(), m_buffer.get(), m_buffer.get() );
	t_data_locker lock( file->get_data_lock() );
	m_file_size = file->get_abstract_file()->filesize();
}

// ----------------------------------------------------------
// streambuf which uses t_shared_file
// ------------------------------------------------------------
t_shared_file_buffer::pos_type t_shared_file_buffer::seekoff(off_type offset, 
															 std::ios_base::seekdir base_offset,
															 std::ios_base::openmode )
{
	t_uint32 position;
	t_uint32 buffer_size = egptr() - m_buffer.get();
	t_uint32 buffer_start = m_file_position - buffer_size;

	switch (base_offset)
	{
		case std::ios_base::beg:
			position = offset;
			break;

		case std::ios_base::cur:
			position = buffer_start + (gptr() - m_buffer.get());
			break;

		case std::ios_base::end:
			position = m_file_size + offset;
			break;
	}
	if (position < 0)
		position = 0;
	if (position > m_file_size)
		position = m_file_size;

	if (position < buffer_start || position > m_file_position)
	{
		setg( m_buffer.get(), m_buffer.get(), m_buffer.get() );
		m_file_position = position;
	}
	else
	{
		setg( m_buffer.get(), position - buffer_start + m_buffer.get(), 
			  m_buffer.get() + buffer_size );
	}
	return position;
}

// ----------------------------------------------------------
// streambuf which uses t_shared_file
// ------------------------------------------------------------
t_shared_file_buffer::pos_type t_shared_file_buffer::seekpos(pos_type position, 
															 std::ios_base::openmode )
{
	if (position < 0)
		position = 0;
	if (position > m_file_size)
		position = m_file_size;

	t_uint32 buffer_size = egptr() - m_buffer.get();
	t_uint32 buffer_start = m_file_position - buffer_size;

	if (position < buffer_start || position > m_file_position)
	{
		setg( m_buffer.get(), m_buffer.get(), m_buffer.get() );
		m_file_position = position;
	}
	else
	{
		setg( m_buffer.get(), t_uint32(position) - buffer_start + m_buffer.get(), 
			  m_buffer.get() + buffer_size );
	}
	return position;
}

// ----------------------------------------------------------
// streambuf which uses t_shared_file
// ------------------------------------------------------------
t_shared_file_buffer::int_type t_shared_file_buffer::underflow()
{
	assert( gptr() == egptr() );

	t_uint32      copy_size = m_file_size - m_file_position;

	if ( copy_size > k_buffer_size )
		copy_size = k_buffer_size;

	m_file->lock();
	m_file->get_abstract_file()->seek( m_file_position, SEEK_SET );
	copy_size = m_file->get_abstract_file()->read( m_buffer.get(), 1, copy_size );
	m_file->unlock();

	m_file_position += copy_size;
	setg( m_buffer.get(), m_buffer.get(), m_buffer.get() + copy_size );
				
	return egptr() > eback() ? traits_type::to_int_type( *gptr() ) : traits_type::eof();
}
